package com.springlearn.springsourcecode.chap21;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockPart;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.DefaultDataBinderFactory;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

public class A21 {


    /*
        1 每个参数处理器能干啥
            是否支持某种参数
            获取参数的值
        2 组合模式在Spring中的体现
        3 @RequestParam @CookieValue等注解中的参数名、默认值，都可以写成活的，即从$() #()中获取
     */
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        // 准备测试Request
        HttpServletRequest multiRequest = mockRequest();

        // 要点1 控制器方法被封装为HandlerMethod
        HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));

        // 要点2 准备对象绑定与类型转换
        ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null,null);

        // 要点3 准备ModelAndViewContainer用来存储中间Model结果
        ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer();

        // 要点4 解析每个参数值
        MethodParameter[] methodParameters = handlerMethod.getMethodParameters();

        for (MethodParameter parameter : methodParameters) {

            // 多个解析器组合，组合器设计模式
            HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
            composite.addResolvers(new RequestParamMethodArgumentResolver(beanFactory, false),
                                   new PathVariableMethodArgumentResolver(),
                                   new RequestHeaderMethodArgumentResolver(beanFactory),
                                   new ServletCookieValueMethodArgumentResolver(beanFactory),
                                   new ExpressionValueMethodArgumentResolver(beanFactory),
                                   new ServletRequestMethodArgumentResolver(),
                                   new ServletModelAttributeMethodProcessor(false),
                                   new RequestResponseBodyMethodProcessor(Arrays.asList(new MappingJackson2HttpMessageConverter())),
                                   new ServletModelAttributeMethodProcessor(true),
                                   new RequestParamMethodArgumentResolver(beanFactory, true)
                    );


            String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(Collectors.joining());
            System.out.println(annotations);

            String str = annotations.length() > 0? " @" + annotations : " ";

            parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());

            if (composite.supportsParameter(parameter)) {
                Object o = composite.resolveArgument(parameter, modelAndViewContainer, new ServletWebRequest(multiRequest), factory);
                System.out.println("[" + parameter.getParameterIndex() + "]" + str  + parameter.getParameterType().getSimpleName() + "  " + parameter.getParameterName() + "->" + o);
            }else {
                System.out.println("[" + parameter.getParameterIndex() + "]" + str  + parameter.getParameterType().getSimpleName() + "  " + parameter.getParameterName());
            }
        }

    }

    private static HttpServletRequest mockRequest(){
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("name1","zhangsan");
        request.setParameter("name2","lisi");
        request.addPart(new MockPart("file","abc","hello".getBytes(StandardCharsets.UTF_8)));
        Map<String, String> uriTemplateVariables = new AntPathMatcher().extractUriTemplateVariables("/test/{id}", "/test/123");
        request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE,uriTemplateVariables);
        request.setContentType("application/json");
        request.setCookies(new Cookie("token","123456"));
        request.setParameter("name","张三");
        request.setParameter("age","18");
        request.setContent("{\\\"name\\\":\\\"李四\\\",\\\"age\\\":20}".getBytes(StandardCharsets.UTF_8));

        return new StandardServletMultipartResolver().resolveMultipart(request);

    }


    static class Controller{

        public void test(
               @RequestParam("name1") String name1, // name1=张三
               String name2,   // name2 = 李四
               @RequestParam("age") int age, // age=18
               @RequestParam(name = "home",defaultValue = "${JAVA_HOME}") String home1,  // spring中获取的数据
               @RequestParam("file") MultipartFile file,  // 上传文件
               @PathVariable("id") int id,   // 路径参数
               @RequestHeader("Content-Type") String header,
               @CookieValue("token") String token,
               @Value("${JAVA_HOME}") String home2,
               HttpServletRequest request,
               @ModelAttribute User user1,
               User user2,
               @RequestBody User user3
        ){

        }


    }

}
